#pragma once

#include <sys/select.h>
#include <unistd.h>
#include <memory>
#include <poll.h>
#include <string>

#include "Socket.hpp"
#include "Log.hpp"

class PollServer
{
private:
    template <class T>
    const T &max(const T &a, const T &b)
    {
        return a > b ? a : b;
    }

public:
    PollServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
    }

    void Init()
    {
        for (int i = 0; i < gnum; i++)
        {
            _fd_events[i].fd = gdefaultfd;
            _fd_events[i].events = 0;
            _fd_events[i].revents = 0;
        }
        _fd_events[0].fd = _listensock->Sockfd();
        _fd_events[0].events = POLLIN;
    }

    void Accepter()
    {
        InetAddr addr;
        int fd = _listensock->Accept(&addr);
        if (fd > 0)
        {
            LOG(INFO, "Get a new link: %s\n", addr.AddrStr().c_str());
            int flag = 1;
            for (int i = 0; i < gnum; i++)
            {
                if (_fd_events[i].fd == gdefaultfd)
                {
                    _fd_events[i].fd = fd;
                    _fd_events[i].events = POLLIN;
                    flag = 0;
                    break;
                }
            }
            if (flag)
            {
                LOG(WARNING, "FD_SET FULL\n");
            }
        }
        else
            LOG(ERROR, "accept error\n");
    }

    void HandlerIO(int i)
    {
        char buffer[1024];
        int n = ::recv(_fd_events[i].fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
            std::string echo_str = "<html><h1>hello bite</h1></html>";   
            std::string echo("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nContent-Length: " +
                             std::to_string(echo_str.size()) + "\r\n\r\n" + echo_str);
            ::send(_fd_events[i].fd, echo.c_str(), echo.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "fd: %d -> quit\n", _fd_events[i].fd);
            ::close(_fd_events[i].fd);
            _fd_events[i].fd = gdefaultfd;
            _fd_events[i].events = _fd_events[i].revents = 0;
            
        }
        else
        {
            LOG(INFO, "fd: %d -> recv error\n", _fd_events[i].fd);
            ::close(_fd_events[i].fd);
            _fd_events[i].fd = gdefaultfd;
            _fd_events[i].events = _fd_events[i].revents = 0;
            
        }
    }

    void HandlerEvent()
    {
        for (int i = 0; i < gnum; i++)
        {
            if (_fd_events[i].fd == gdefaultfd)
                continue;
            if (_fd_events[i].revents & POLLIN)
            {
                if (_listensock->Sockfd() == _fd_events[i].fd)
                {
                    Accepter();
                }
                else
                {
                    HandlerIO(i);
                }
            }
        }
    }

    void Loop()
    {
        while (1)
        {
            int timeout = 5000;
            int n = ::poll(_fd_events, gnum, timeout);
            switch (n)
            {
            case 0:
                std::cout << "time out" << std::endl;
                break;
            case -1:
                std::cout << "select error" << std::endl;
                break;
            default:
                std::cout << "haved event ready, n:" << n << std::endl;
                HandlerEvent();
                PrintFD();
                break;
            }
        }
    }

    void PrintFD()
    {
        std::cout<<"fd_list: ";
        for (int i = 0; i < gnum; i++)
        {
            if (_fd_events[i].fd != gdefaultfd)
            {
                std::cout<<_fd_events[i].fd<<" ";
            }
        }
        std::cout<<std::endl;
    }

    ~PollServer() 
    {
        _listensock->Close();
    }

private:
    const static int gnum = sizeof(fd_set) * 8;
    const static int gdefaultfd = -1;
    std::unique_ptr<Socket> _listensock;
    uint16_t _port;
    struct pollfd _fd_events[gnum];
};
